import type { PluginOption } from 'vite'
import fs from 'node:fs'
import path from 'node:path'
import { exec } from 'node:child_process' // 添加这行导入
import archiver from 'archiver'
import dayjs from 'dayjs'
// import { filesize } from 'filesize'
// import open from 'open' // 可以删除这行注释
import type { ResolvedConfig } from "vite";

// 修改接口定义
interface VitePluginArchiverOptions {
    /**
   * 输出目录，默认与 Vite 的输出目录(build.outDir)相同，有需要可以自定义
   * 查看：https://cn.vitejs.dev/config/build-options#build-outdir
   *
   * @default ResolvedConfig.build.outDir
   */
    outDir?: string
    /**
     * 压缩文件名称，默认与 Vite 的输出目录(build.outDir)相同，有需要可以自定义
     * @default ResolvedConfig.build.outDir
     */
    fileName?: string
    /**
     * 压缩文件名称日期连接符，默认使用 '_' 分隔
     * @default '_'
     */
    fileNameDateseparator?: string
    /**
   * 压缩文件名日期部分格式，使用 dayjs 格式化
   * 为 false 时，不拼接日期到文件名
   * @default 'YYYY-MM-DD-HH-mm-ss' 
   */
    formatTemplate?: string | false
    /**
     * 源文件目录，默认与 Vite 的输出目录(build.outDir)相同，有需要可以自定义，但通常不需要
     * 查看：https://cn.vitejs.dev/config/build-options#build-outdir
     *
     * @default ResolvedConfig.build.outDir
     */
    sourceDir?: string
    /**
     * 是否将源文件目录也加入压缩包，或指定压缩包中的目录名称
     * 当设置为 true 时，压缩包中会包含源目录，默认使用压缩包文件名作为目录名
     * 当设置为 false 时，只会添加源目录中的文件和子目录
     * 当设置为 'sourceDir' 字符串时，将使用源文件目录名作为目录名
     * 当设置为字符串时，将使用该字符串作为目录名
     * @default false
     */
    includeSourceDir?: boolean | string
    /**
     * 压缩类型，支持 zip 和 tar
     *
     * @default 'zip'
     */
    archiveType?: archiver.Format
    /**
     * zip 选项
     * 查看：https://www.archiverjs.com/docs/archiver#zip-options
     *
     * @default { zlib: { level: 9 } }
     */
    archiveZipOptions: archiver.ZipOptions
    /**
     * tar 选项
     * 查看：https://www.archiverjs.com/docs/archiver#tar-options
     *
     * @default { gzip: true, gzipOptions: { level: 9 } }
     */
    archiveTarOptions: archiver.TarOptions

    /**
     * 完成时是否打开存档文件所在目录
     *
     * @default false
     */
    open: boolean
    /**
     * 当输出目录不等于 Vite 的输出目录时，是否清空输出目录
     * 注意：这会删除输出目录中的所有文件，请谨慎使用
     * @default true
     */
    cleanOutDir?: boolean
}

export default function (userOptions: Partial<VitePluginArchiverOptions> = {}): PluginOption {
    const options: VitePluginArchiverOptions = {
        // default options
        ...{
            archiveType: 'zip',
            archiveZipOptions: {
                zlib: { level: 9 },
            },
            archiveTarOptions: {
                gzip: true,
                gzipOptions: { level: 9 },
            },
            formatTemplate: 'YYYY-MM-DD-HH-mm-ss',
            open: false,
            // 添加新配置项的默认值
            fileNameDateseparator: '_',
            cleanOutDir: true, // 默认直接清空输出目录
            includeSourceDir: false, // 默认不包含源目录本身
        },
        // user options
        ...userOptions,
    };
    let viteConfig: ResolvedConfig
    return {
        name: 'vite-plugin-archiver',
        apply: 'build',
        enforce: 'post',
        configResolved(config) {
            options.sourceDir ??= config.build.outDir
            options.outDir ??= config.build.outDir
            options.fileName ??= path.basename(config.build.outDir)
            viteConfig = config
        },
        // 2. 修改 closeBundle 中添加文件到压缩包的逻辑
        closeBundle: {
            order: 'post',
            handler() {
                const archive = archiver(options.archiveType!, {
                    ...(options.archiveType === 'zip' && options.archiveZipOptions),
                    ...(options.archiveType === 'tar' && options.archiveTarOptions),
                })

                // 根据新的配置生成文件名
                let baseName = options.fileName
                // 如果formatTemplate不是false，则添加日期部分
                if (options.formatTemplate !== false) {
                    baseName += `${options.fileNameDateseparator || ''}${dayjs().format(options.formatTemplate)}`
                }
                // 添加文件扩展名
                const fileName = `${baseName}.${options.archiveType === 'zip' ? 'zip' : 'tar.gz'}`

                // 使用outDir作为输出路径
                const outputPath = path.join(viteConfig.root, options.outDir!)
                // 确保输出目录存在
                if (!fs.existsSync(outputPath)) {
                    fs.mkdirSync(outputPath, { recursive: true })
                }

                // 获取Vite输出目录的绝对路径
                const viteOutDirAbs = path.resolve(viteConfig.root, viteConfig.build.outDir)
                // 获取输出目录的绝对路径
                const outputDirAbs = path.resolve(viteConfig.root, options.outDir!)

                // 当输出目录不等于Vite的输出目录时，如果设置了cleanOutDir为true，则清空输出目录
                if (outputDirAbs !== viteOutDirAbs && options.cleanOutDir) {
                    try {
                        const files = fs.readdirSync(outputPath)
                        for (const file of files) {
                            const filePath = path.join(outputPath, file)
                            const stat = fs.statSync(filePath)
                            if (stat.isDirectory()) {
                                fs.rmSync(filePath, { recursive: true, force: true })
                            } else {
                                fs.unlinkSync(filePath)
                            }
                        }
                    } catch (error) {
                        console.error('Failed to clean output directory:', error)
                    }
                }

                const output = fs.createWriteStream(path.join(outputPath, fileName))
                output.on('close', () => {
                    // eslint-disable-next-line no-console
                    console.log(`Archiver file: ${path.join(outputPath, fileName)} `);
                    //   (${filesize(archive.pointer(), { standard: 'jedec' })})
                });
                archive.pipe(output)

                // 统一使用glob方法处理文件添加，并始终排除正在创建的压缩文件
                // 根据includeSourceDir配置决定如何添加文件
                let dirNameToUse: any = false
                if (options.includeSourceDir === true) {
                    // 当设置为true时，使用压缩包文件名（不包含扩展名）作为目录名
                    dirNameToUse = baseName
                } else if (typeof options.includeSourceDir === 'string') {
                    // 当设置为字符串时，判断是否是特殊值 'sourceDir'
                    if (options.includeSourceDir === 'sourceDir') {
                        // 使用源文件目录名作为目录名
                        dirNameToUse = path.basename(options.sourceDir!)
                    } else {
                        // 直接使用该字符串作为目录名
                        dirNameToUse = options.includeSourceDir
                    }
                }

                // 使用directory方法并通过EntryDataFunction过滤压缩文件
                archive.directory(options.sourceDir!, dirNameToUse, (entry) => {
                    // 排除正在创建的压缩文件
                    if (entry.name === path.basename(fileName)) {
                        return false;
                    }
                    return entry;
                })
                // 在 archive.finalize() 后修改打开功能的实现
                archive.finalize()
                // 替换注释掉的 open 代码
                if (options.open) {
                    // 使用 Node.js 内置的 child_process 模块实现跨平台打开功能
                    let command = ''
                    const windowsEscapedPath = outputPath.replace(/\\/g, '\\\\')

                    // 根据不同操作系统使用不同的命令
                    if (process.platform === 'win32') {
                        // Windows 系统
                        command = `start "" "${windowsEscapedPath}"`
                    } else if (process.platform === 'darwin') {
                        // macOS 系统
                        command = `open "${outputPath}"`
                    } else {
                        // Linux 或其他类 Unix 系统
                        command = `xdg-open "${outputPath}" || gio open "${outputPath}" || x-www-browser "${outputPath}" || gnome-open "${outputPath}" || kde-open "${outputPath}"`
                    }

                    // 执行命令
                    exec(command, (error) => {
                        if (error) {
                            console.error(`Failed to open directory: ${error.message}`)
                        }
                    })
                }
            },
        },
    }
}
